package org.jvm.rtda.heap;

import org.jvm.rtda.heap.classmember.Method;

import java.util.*;

/**
 * @author 海燕
 * @date 2023/2/11
 */
public class MethodUtil {

    /**
     * 在类中查找方法
     * 1、从类继承结构中查找
     * 2、从接口中查找
     *
     * @param klass
     * @param name
     * @param descriptor
     * @return
     */
    public static Method lookupMethod(Klass klass, String name, String descriptor) {
        Method method = lookupMethodInClass(klass, name, descriptor);
        /**
         * 至于为什么非接口引用要在接口中查找方法，设想一个场景
         * 被引用类是抽象类继承了接口，但没有实现接口中的方法
         * 此时实际引用的就是接口的方法
         */
        if (method == null) {
            method = lookupMethodInInterfaces(klass.getInterfaces(), name, descriptor);
        }
        return method;
    }

    /**
     * 从类实现的接口中寻找方法
     *
     * @param interfaces
     * @param name
     * @param descriptor
     * @return
     */
    public static Method lookupMethodInInterfaces(Klass[] interfaces, String name, String descriptor) {
        if (interfaces == null || interfaces.length == 0) {
            return null;
        }
        for (Klass i : interfaces) {
            Method method = Arrays.stream(i.getMethods())
                    .filter(item -> item.getName().equals(name) && item.getDescriptor().equals(descriptor))
                    .findAny().orElse(null);
            if (method != null) {
                return method;
            }
        }
        for (Klass i : interfaces) {
            Method method = lookupMethodInInterfaces(i.getInterfaces(), name, descriptor);
            if (method != null) {
                return method;
            }
        }
        return null;
    }

    /**
     * 从类的类继承结构中寻找方法
     *
     * @param klass
     * @param name
     * @param descriptor
     * @return
     */
    public static Method lookupMethodInClass(Klass klass, String name, String descriptor) {
        if (klass == null) {
            return null;
        }
        Method[] methods = Optional.ofNullable(klass.getMethods()).orElse(new Method[0]);
        Method method = Arrays.stream(methods)
                .filter(item -> item.getName().equals(name) && item.getDescriptor().equals(descriptor))
                .findAny().orElse(null);
        if (method == null) {
            return lookupMethodInClass(klass.getSuperClass(), name, descriptor);
        } else {
            return method;
        }
    }
}
